Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix stuck soldiers leading to crashes in drawing code #1706

Open
wants to merge 22 commits into
base: master
Choose a base branch
from

Conversation

Flamefire
Copy link
Member

Fixes #1695
Fixes #1668

The cause is in both issues the same:

One soldier involved in an upcoming free fight is waiting for the other. That aborts the free fight as the spot becomes unreachable due to a removed road over water (either manually or by a captured building)
The waiting soldier changes state but doesn't start moving and as he isn't moving already there is no event queued that would make it handle the new (moving) state.
This leads to it being stuck there forever and also crashes the draw code which doesn't expect a supposedly walking soldier not having a walk-event.

Implemented solution:

Split the AbortFreeFight method in 2:

  • virtual method to determine the next state -> This ensures derived classes change state
  • base method to reset the free fight state for both soldiers and let them continue -> Actual abort-fight handling in one place

For the aborting soldier effectively nothing is changed.
The other soldier gets a simulated walk event if he was waiting to handle the new state.

As the 2 soldiers might get involved in another fight with each other both their "fight states" are cleaned up first to establish a valid state before the other soldier gets triggered.

As this was a bit tricky to find and create a test case to reproduce this I added quite a bit refactoring:

General:

  • Method to jump to a specific position in the map-debug window and disable FoW when opening that window to avoid having to search & scroll to the spot where it crashes
  • During tests I ended up accidentally creating a copy of a soldier which was very confusing and hard to find -> Avoid that at the root as much as possible
  • Enhanced the range class start iterating not only from zero but any value
  • Easier search for DescIdx matching conditions (useful especially for tests to quickly find e.g. water)
  • DescriptionVector to have a std::vector but indexed only by DescIdx

To understand what is happening with (fighting) soldiers I translated and fixed comments and documentation and some smaller refactoring to make it easier to read. Especially use of an enum instead of uint8 and references where NULL isn't allowed.

We usually want to browse the map anyway so just combine showing the
window and revealing the map.
For debugging we often want to view a specific position.
Make this easy by specifying the position as X-Y and Go-To with RETURN.
TAB can be used to change between the edits which also clears the newly
activated one such that new coordinates can be entered.
As we store GameObject references by pointer we must not copy them.
The only exception is for soldiers where we convert a passive soldier
to an attacker or defender by copying the base attributes.

Mark any copy constructor we don't need as deleted and the others as
explicit and protected.
In some cases we need to start at e.g. 1 not 0.
Also make use of C++17 CTAD and remove the factory method.
Allow to easier understand the soldier logic by translating the comments and docstrings improving the wording while doing that.
Also small (mostly syntactical) changes to follow the logic more easily
Better capture meaning as it implies an action not just a search.
`CancelSeaAttack` already calls `Abrogate`
Easier to understand and validate.
Also refactor one use site a bit translating the comments.
Make the decisions clearer by showing the symmetries and the few
different cases.
Translate all remaining comments.
Refactor code of type
```
if(arrived) {
  //handle arrived
  return
}
// handle not arrived
```

to

```
if(arrived) {
  // handle arrived
} else {
  // handle not arrived
}
```

Especially for small or similar code it emphasizes the either-or logic.
Communicate that those must not be NULL
The method is called when a free fight cannot be started not after it
has ended, so reflect this in the name.
In some places we need e.g. "any water" terrain or in general a single
or all descriptors matching a condition.
Add `find` and `findAll` methods to the containers and add and use the
explicit bool operator.
Both in combination avoid using the raw `value` member of the `DescIdx`
in most cases.
Also use the bool operator of `DescIdx` for invalid edge-indices.
Create 2 tests for free fights aborted by either the attacker or defender.
This reproduces issue Return-To-The-Roots#1668
When one soldier involved in an upcoming free fight is waiting for the
other and that aborts the free fight for any reason, e.g. when the spot
becomes unreachable, the waiting soldier changes state but doesn't start
moving and as he isn't moving already there is no event queued that
would make it handle the new (moving) state.
This leads to it being stuck there forever and also crashes the draw
code which doesn't expect a supposedly walking soldier not having a walk
event.

Split the `AbortFreeFight` method in 2:
- virtual method to determine the next state
- base method to reset the free fight state for both soldiers and let
  them continue

For the aborting soldier effectively nothing is changed.
The other soldier gets a simulated walk event if he was waiting to
handle the new state.

As the 2 soldiers might get involved in another fight with each other
both their "fight states" are cleaned up first to establish a valid
state before the other soldier gets triggered.
@Flamefire Flamefire requested a review from Flow86 October 19, 2024 12:02
@Flow86
Copy link
Member

Flow86 commented Oct 28, 2024

rebase necessary, but other than that, lgtm

@Flamefire
Copy link
Member Author

Done. IIRC you can approve which stays active after the update/rebase such that it can be merged right away.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants